import getGlobalData from "@/tool/getGlobalData"
// @ts-ignore
import { loadModule } from 'vue3-sfc-loader'
import * as Vue from "vue";
import { getConfigUrl, getAppMain } from "@/api/application"
import type { AxiosResponse } from 'axios'

const options = {
    moduleCache: { vue: Vue },
    async getFile(url: string) {
        const res = await fetch(url);
        if (!res.ok)
            throw Object.assign(new Error(res.statusText + ' ' + url), { res });
        return {
            type: ".vue",
            getContentData: (asBinary: boolean) => asBinary ? res.arrayBuffer() : res.text(),
        }
    },
    addStyle(textContent: string) {
        const style = Object.assign(document.createElement('style'), { textContent });
        const ref = document.head.getElementsByTagName('style')[0] || null;
        document.head.insertBefore(style, ref);
    }
}
class application {
    name: string | undefined
    version: string | undefined
    size: number | undefined
    packageName: string | undefined
    installUrl: string | undefined
    data: string | undefined
    appId: string | undefined
    constructor() { }

    init(packageName: string) {
        if (!packageName) throw new Error("为什么不传packageName！")
        return new Promise(async (resolve) => {
            let localApp = await this.getLocalApp(packageName)
            if (localApp) {
                await this.checkUpdate(localApp)
                await this.loadLocal(localApp)
            } else {
                await this.download(packageName)
            }
            resolve(void 0)
        })
    }

    el() {
        this.installLocal()
        return loadModule(this.installUrl, options)
    }

    private async download(packageName: string) {
        this.appId = packageName
        let { data: appData } = await getConfigUrl(packageName)
        await this.install(appData)
    }
    update(appData: application) {
        return this.install(appData)
    }
    private async install(appData: application) {
        this.name = appData.name
        this.packageName = appData.packageName
        this.version = appData.version
        let main = await getAppMain(appData.packageName!)
        this.size = main.data.length
        this.data = main.data
        this.installUrl = this.ctrBlobUrl(main.data)
    }
    private getLocalApp(packageName: string) {
        return getGlobalData("localforage").getItem(packageName)
    }

    private installLocal() {
        // getGlobalData("localforage").removeItem(this.packageName,()=>)
        return getGlobalData("localforage").setItem(this.packageName, { t: Math.random(), appId: this.packageName, name: this.name, packageName: this.packageName, version: this.version, size: this.size, data: this.data })
    }
    private ctrBlobUrl(data: string) {
        return URL.createObjectURL(new Blob([data]))
    }
    private getAPPConfig(packageName?: string) {
        let config
        try {
            config = getConfigUrl(packageName || this.packageName!)
        } catch {
            return -1
        }

        return config
    }
    /**
     * 检查是否需要更新
     * @param localData 本地config
     */
    private async checkUpdate(localData: application) {
        let re = await this.getAPPConfig(localData.packageName)
        if (re == -1) throw new Error(`Application ${this.packageName} Error`)

        let { data: appData } = re as AxiosResponse<any, any>

        let needUpdate = this.compareVersions(appData.version, localData.version!)
        if (needUpdate > 0 || !localData.data) return this.update(appData)
        else return localData
    }
    /**
     * 版本号比对
     * @param version1 版本号1
     * @param version2 版本号2
     * @returns 1 1号更大 -1 号更大 0 一样大
     */
    private compareVersions(version1: string, version2: string) {
        let arr1 = version1.split('.');
        let arr2 = version2.split('.');

        for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
            let num1 = parseInt(arr1[i] || '0');
            let num2 = parseInt(arr2[i] || '0');
            if (num1 < num2) { return -1; } else if (num1 > num2) { return 1; }
        }
        return 0;
    }

    private async loadLocal(localApp:application) {
        return new Promise((resolve) => {
            if (!this.packageName) {
                this.appId = localApp.appId
                this.data = localApp.data
                this.version = localApp.version
                this.packageName = localApp.packageName
                this.size = localApp.size
                this.name = localApp.name
            }
            this.installUrl = this.ctrBlobUrl(this.data!)
            resolve(void 0)
        })
    }
}

export default application